﻿//INSTANT C# NOTE: Formerly VB project-level imports:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Linq;
using System.Xml.Linq;

using Microsoft.IdentityModel.Protocols.WSFederation.Metadata;
using Microsoft.IdentityModel.Protocols.WSIdentity;
using System.ServiceModel;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using Microsoft.Win32;
using System.IO;

namespace FederationMetadataEditor
{
	public partial class frmMain
	{

		internal frmMain()
		{
			InitializeComponent();
        }
		private MetadataBase manifestdb = null;
		private EntityDescriptor _manifestEntity = null;


		public EntityDescriptor ManifestEntity
		{
			get
			{
				return (EntityDescriptor)manifestdb;
			}
		}

        private bool IsWindowsSDKInstalled()
        {
            var key = @"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
            var regKey = Registry.LocalMachine.OpenSubKey(key);
            if (regKey != null)
            {
                return true;
            }

            return false;
        }




		private void cmdBrowse_Click(object sender, System.EventArgs e)
		{
			if (this.OpenMetadataFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				this.fldFilePath.Text = OpenMetadataFileDialog.FileName;
			}
		}

		private void cmdLoad_Click(object sender, System.EventArgs e)
		{
			if (!(string.IsNullOrEmpty(this.fldFilePath.Text.Trim())))
			{
				if (System.IO.File.Exists(this.fldFilePath.Text.Trim()))
				{
					// TODO: Add some error handling code here
					MetadataSerializer mds = new MetadataSerializer();
					

                    try
                    {
                        using (System.IO.FileStream fs = System.IO.File.OpenRead(this.fldFilePath.Text))
                        {
                            manifestdb = mds.ReadMetadata(fs);

                            if (manifestdb != null && (manifestdb) is EntityDescriptor)
                            {
                                LoadEntity((EntityDescriptor)manifestdb);
                            }
                            else
                            {
                                DialogResult result = MessageBox.Show("Error reading manifest file!  Would you like to fix this automatically?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
                                if (result == System.Windows.Forms.DialogResult.Yes)
                                {
                                    RemoveDigitalSignature(this.fldFilePath.Text);
                                }//if
                            } 
                        }//using
                    }
                    catch (CryptographicException ex)
                    {
                        DialogResult result = MessageBox.Show("Error reading manifest file!  Would you like to fix this automatically?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
                        if (result == System.Windows.Forms.DialogResult.Yes)
                        {
                            RemoveDigitalSignature(this.fldFilePath.Text);
                        }//if
                        else
                        {
                            MessageBox.Show("Manifest file was not altered." + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }//else
                    }
                    finally
                    {
                        
                    }//finally
					
				}
				else
				{
					MessageBox.Show("Manifest file doesn't exist!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}
			else
			{
				MessageBox.Show("You have to select a manifest file!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void LoadEntity(EntityDescriptor myEntity)
		{
			this.issuerUri_TextBox.Text = myEntity.EntityId.Id;
			if (myEntity.RoleDescriptors.Count > 0)
			{
                if ((myEntity.RoleDescriptors.ElementAt(0)) is ApplicationServiceDescriptor)
				{
                    ApplicationServiceDescriptor appDescriptor = (ApplicationServiceDescriptor)(myEntity.RoleDescriptors.ElementAt(0));
					this.serviceName_TextBox.Text = appDescriptor.ServiceDescription;
					this.ActiveClaims.Clear();
					foreach (var claim in appDescriptor.ClaimTypesRequested)
					{
						DisplayClaim displayClaim = claim;
						this.ActiveClaims.Add(CustomDisplayClaim.FromDisplayClaim(ref displayClaim));
					}
					this.EndPointsDataSet.PassiveEndPoints.Rows.Clear();
                    foreach (EndpointAddress endpoint in appDescriptor.Endpoints)
                    {
                        DataRow newRow = this.EndPointsDataSet.PassiveEndPoints.NewRow();
                        newRow["Url"] = endpoint.Uri.OriginalString;
                        this.EndPointsDataSet.PassiveEndPoints.Rows.Add(newRow);
                    }
					this.EndPointsDataSet.ActiveEndPoints.Rows.Clear();
					foreach (EndpointAddress endpoint in appDescriptor.TargetScopes)
					{
						DataRow newRow = this.EndPointsDataSet.ActiveEndPoints.NewRow();
						newRow["Url"] = endpoint.Uri.OriginalString;
						this.EndPointsDataSet.ActiveEndPoints.Rows.Add(newRow);
					}
					if (appDescriptor.Keys.Count > 0)
					{
						if (appDescriptor.Keys.Count > 1)
						{
							MessageBox.Show("Metadata file contains more than one certificate files! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
						}
						if (appDescriptor.Keys.ElementAt(0).KeyInfo.Count > 0)
						{
							if ((appDescriptor.Keys.ElementAt(0).KeyInfo[0]) is System.IdentityModel.Tokens.X509RawDataKeyIdentifierClause)
							{
								try
								{
									selectedSigningCertificate = new X509Certificate2(((System.IdentityModel.Tokens.X509RawDataKeyIdentifierClause)(appDescriptor.Keys.ElementAt(0).KeyInfo[0])).GetX509RawData());
									X509Certificate2 tempVar3 = selectedSigningCertificate;
									LoadInstalledCertificates(ref tempVar3);
									selectedSigningCertificate = tempVar3;
								}
								catch
								{
									MessageBox.Show("Couldn't retrieve the certificate!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
								}
							}
						}

					}
				}
				else
				{
                    MessageBox.Show("The first role descriptor must be a ApplicationServiceDescriptor for a relying party application!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				}
			}
			else
			{
				MessageBox.Show("No role descriptors are specified in the metadata file!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}

			if (myEntity.Contacts.Count > 0)
			{
                btnAddContact.Enabled = false;
				if (myEntity.Contacts.Count > 1)
				{
					MessageBox.Show("Metadata file contains more than one contact info! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				}
				Microsoft.IdentityModel.Protocols.WSFederation.Metadata.ContactPerson tempVar4 = myEntity.Contacts.ElementAt(0);
				Contact_CompanyTextBox.Text = tempVar4.Company;
				if (tempVar4.EmailAddresses.Count > 0)
				{
					if (tempVar4.EmailAddresses.Count > 1)
					{
						MessageBox.Show("Metadata file contains more than one email for the contact person! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}
					this.Contact_EmailTextBox.Text = tempVar4.EmailAddresses.ElementAt(0);
				}
				this.Contact_GivenNameTextBox.Text = tempVar4.GivenName;
				this.Contact_SurnameTextBox.Text = tempVar4.Surname;
				if (tempVar4.TelephoneNumbers.Count > 0)
				{
					if (tempVar4.TelephoneNumbers.Count > 1)
					{
						MessageBox.Show("Metadata file contains more than one telephone number for the contact person! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}
					this.Contact_TelephoneTextBox.Text = tempVar4.TelephoneNumbers.ElementAt(0);
				}
				this.Contact_TypeComboBox.SelectedItem = tempVar4.Type;

                //Organizational checks
				if (myEntity.Organization.DisplayNames.Count > 0)
				{
					if (myEntity.Organization.DisplayNames.Count > 1)
					{
						MessageBox.Show("Metadata file contains more than one display name for the company! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}
					this.Company_DisplayName.Text = myEntity.Organization.DisplayNames[0].Name;
				}
				if (myEntity.Organization.Names.Count > 0)
				{
					if (myEntity.Organization.Names.Count > 1)
					{
						MessageBox.Show("Metadata file contains more than one name for the company! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}
					this.Company_Name.Text = myEntity.Organization.Names[0].Name;
				}
				if (myEntity.Organization.Urls.Count > 0)
				{
					if (myEntity.Organization.Urls.Count > 1)
					{
						MessageBox.Show("Metadata file contains more than one Url for the company! This information will be lost if you save...", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}
					this.Company_URL.Text = myEntity.Organization.Urls[0].Uri.OriginalString;
				}

			}
			else if (myEntity.Contacts.Count == 0)
			{
				//REM No Need to load Contact Info
				//MsgBox("Metadata file doesn't contain any contact info!", MsgBoxStyle.Exclamation, "Warning")
                btnAddContact.Enabled = true;
			}

		}

		public CustomDisplayClaimCollection ActiveClaims
		{
			get
			{
				return (CustomDisplayClaimCollection)this.DisplayClaimCollectionBindingSource.DataSource;
			}
		}

		private void frmMain_Load(object sender, System.EventArgs e)
		{
			this.Contact_TypeComboBox.Items.Add(ContactType.Administrative);
			this.Contact_TypeComboBox.Items.Add(ContactType.Billing);
			this.Contact_TypeComboBox.Items.Add(ContactType.Other);
			this.Contact_TypeComboBox.Items.Add(ContactType.Support);
			this.Contact_TypeComboBox.Items.Add(ContactType.Technical);
			this.Contact_TypeComboBox.Items.Add(ContactType.Unspecified);
			this.DisplayClaimCollectionBindingSource.DataSource = new CustomDisplayClaimCollection();
		}

		private void cmdAddNewClaim_Click(object sender, System.EventArgs e)
		{
			ActiveClaims.Add(new CustomDisplayClaim {claimType = "http://"});
		}

		public X509Certificate2 selectedSigningCertificate {get; set;}

		private void cmdSelectInstalledCertificate_Click(object sender, System.EventArgs e)
		{
			X509Store store = new X509Store(StoreLocation.LocalMachine);
			store.Open(OpenFlags.OpenExistingOnly);
			X509Certificate2Collection SelectedCertificates = X509Certificate2UI.SelectFromCollection(store.Certificates, "Please select the signing certificate", "The selected certificate will be used to sign the federation metadata document and to identify the STS on the RP.", X509SelectionFlag.SingleSelection);
			if (SelectedCertificates.Count > 0)
			{
				X509Certificate2 tempVar = SelectedCertificates[0];
				AssignSelectedCertificate(ref tempVar);
					SelectedCertificates[0] = tempVar;
			}
			store.Close();
		}

		private void LoadInstalledCertificates(ref X509Certificate2 cer)
		{
			X509Store store = new X509Store(StoreLocation.LocalMachine);
			store.Open(OpenFlags.OpenExistingOnly);
			X509Certificate2Collection certColl = store.Certificates;

			//Set the original certificate Thumbprint for display and reference purposes
			SetOriginalCertificateThumbprint(cer);

			//Set the default installed certificate as the current certificate used in signing
			X509Certificate2 installedCertificate = cer;

			//Iterate through the Certificate Store Collection to find the matching installed certificate
			foreach (var x509 in certColl)
			{
				if (x509.Subject == cer.Subject)
				{
					//Set the installed certificate to the X509 Certificate contained in the Certificate Store
					installedCertificate = x509;
					//Set the original certificate to the X509 Certificate contained in the Certificate Store
					//so that the Private Key is retained through the Signing operation (since the parameter is passed ByRef)
					cer = x509;
				}
			}

			store.Close();
			AssignSelectedCertificate(ref installedCertificate);
		}

		private void SetOriginalCertificateThumbprint(X509Certificate2 cer)
		{
			this.txtOriginalThumbprint.Text = cer.Thumbprint;
		}

		private void AssignSelectedCertificate(ref X509Certificate2 cer)
		{
			this.selectedSigningCertificate = cer;
			this.Certificate_Signing_Subject.Text = this.selectedSigningCertificate.Subject;
			this.txtThumbprintValue.Text = this.selectedSigningCertificate.Thumbprint;
			this.chkSignManifest.Enabled = this.selectedSigningCertificate.HasPrivateKey;
			this.chkSignManifest.Checked = this.selectedSigningCertificate.HasPrivateKey;
		}

		private void cmdSelectCertificateFile_Click(object sender, System.EventArgs e)
		{

            frmSelectCertificateFile frmSelectCert = new frmSelectCertificateFile();
            if (frmSelectCert.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if (frmSelectCert.selectedSigningCertificate != null)
                {
                    var selectedCert = frmSelectCert.selectedSigningCertificate;

                    AssignSelectedCertificate(ref selectedCert);
                }//if
            }//if
        }//event: cmdSelectCertificateFile_Click
            

		private void cmdSave_Click(object sender, System.EventArgs e)
		{
			if (!(string.IsNullOrEmpty(this.fldFilePath.Text.Trim())))
			{
				try
				{
					System.Collections.Specialized.StringCollection activeEndpoints = new System.Collections.Specialized.StringCollection();
					foreach (EndPointsDataSet.ActiveEndPointsRow row in this.EndPointsDataSet.ActiveEndPoints.Rows)
					{
						activeEndpoints.Add(row.Url);
					}
					System.Collections.Specialized.StringCollection passiveEndpoints = new System.Collections.Specialized.StringCollection();
                    foreach (EndPointsDataSet.PassiveEndPointsRow row in this.EndPointsDataSet.PassiveEndPoints.Rows)
                    {
                        passiveEndpoints.Add(row.Url);
                    }

					if (this.ManifestEntity.Contacts.Count == 0)
					{
						MetadataGenerator generator = new MetadataGenerator(this.issuerUri_TextBox.Text, this.serviceName_TextBox.Text, activeEndpoints, passiveEndpoints, ActiveClaims, this.selectedSigningCertificate, this.chkSignManifest.Checked);
						generator.GenerateAsFile(this.fldFilePath.Text);
					}
					else if (this.ManifestEntity.Contacts.Count > 0)
					{
						ContactPerson contactPerson = new ContactPerson {Company = Contact_CompanyTextBox.Text, GivenName = Contact_GivenNameTextBox.Text, Surname = Contact_SurnameTextBox.Text, Type = (ContactType)Contact_TypeComboBox.SelectedItem};
						contactPerson.EmailAddresses.Add(this.Contact_EmailTextBox.Text);
						contactPerson.TelephoneNumbers.Add(this.Contact_TelephoneTextBox.Text);
						Organization organization = new Organization();
						organization.DisplayNames.Add(new LocalizedName(Company_DisplayName.Text, System.Globalization.CultureInfo.InvariantCulture));
						organization.Names.Add(new LocalizedName(Company_Name.Text, System.Globalization.CultureInfo.InvariantCulture));
						organization.Urls.Add(new LocalizedUri(new Uri(Company_URL.Text), System.Globalization.CultureInfo.InvariantCulture));

						MetadataGenerator generator = new MetadataGenerator(contactPerson, organization, this.issuerUri_TextBox.Text, this.serviceName_TextBox.Text, activeEndpoints, passiveEndpoints, ActiveClaims, this.selectedSigningCertificate, this.chkSignManifest.Checked);
						generator.GenerateAsFile(this.fldFilePath.Text);
					}
				}
				catch (Exception ex)
				{
					//TODO: customize errors
					MessageBox.Show(string.Format("An error occurred saving the file. The message is {0}!", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
				MessageBox.Show("Save completed successfully!", "Save completed", MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
			else
			{
				MessageBox.Show("You must specify the manifest file path!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void txtCopiedThumbprint_TextChanged(object sender, System.EventArgs e)
		{
			string copiedThumbPrint = null;

			copiedThumbPrint = txtCopiedThumbprint.Text;

			string[] thumbprintChars = copiedThumbPrint.Split(new char[] {' '});
			string parsedThumbPrint = string.Concat(thumbprintChars);

			txtParsedThumbprint.Text = parsedThumbPrint.ToUpper();

		}

		private void cmdDisplayClaimTypes_Click(object sender, System.EventArgs e)
		{
			if (frmDisplayClaimTypes.DefaultInstance.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				//Me.fldFilePath.Text = OpenMetadataFileDialog.FileName
			}
		}

        /// <summary>
        /// Removes an Invalid Digital Signature
        /// </summary>
        private void RemoveDigitalSignature(string strFilePath)
        {
            XDocument xDoc = XDocument.Load(strFilePath);
            XNamespace ns = "urn:oasis:names:tc:SAML:2.0:metadata";

            var digitalSignaturePrefix = ns + "ds";
            var digitalSignatureElement = "Signature";
            var keyDescriptorElement = "KeyDescriptor";

            var digitalSignatureNode = from fedMetaData in xDoc.Root.Elements().Where(signature => signature.Name.LocalName == digitalSignatureElement)
                                       select fedMetaData;

            //var keyDescriptorNode = from fedMetaData in xDoc.Root.Elements().Where(keyDescriptor => keyDescriptor.Name.LocalName == keyDescriptorElement)
                                       //select fedMetaData;

            digitalSignatureNode.Remove();
            //keyDescriptorNode.Remove();

            //Save the changes
            xDoc.Save(strFilePath);
            
        }//method: RemoveInvalidDigitalSignature()

	private static frmMain _DefaultInstance;
	public static frmMain DefaultInstance
	{
		get
		{
			if (_DefaultInstance == null)
				_DefaultInstance = new frmMain();

			return _DefaultInstance;
		}
	}

    private void btnGenerateNewCert_Click(object sender, EventArgs e)
    {
         
            if (!this.IsWindowsSDKInstalled())
            {
                MessageBox.Show("This feature requires some command line tools from the Windows SDK like makecert.exe. Please download the SDK from http://go.microsoft.com/fwlink/?LinkID=191420", "Windows SDK Not installed", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            using (var form = new GenerateCertificateForm())
            {
                DialogResult result = form.ShowDialog(this);
                X509Certificate2 newSigningCertificate = null;

                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    byte[] cert = null;
                    string certFilePath = new FileInfo(form.CertificateFileName).FullName;
                    if (File.Exists(certFilePath))
                    {
                        cert = System.IO.File.ReadAllBytes(certFilePath);
                        if (! string.IsNullOrEmpty(form.CertificatePassword.Trim()))
                        {
                            newSigningCertificate = new X509Certificate2(cert, form.CertificatePassword);
                        }
                        else
                        {
                            newSigningCertificate = new X509Certificate2(cert);
                        }
                    }//if
                    AssignSelectedCertificate(ref newSigningCertificate);
                    
                }//if
            }//using
    }

    /// <summary>
    /// Copies the Thumbprint value in the textbox
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void cmdCopyThumbprint_Click(object sender, EventArgs e)
    {
        txtThumbprintValue.SelectAll();
        txtThumbprintValue.Copy();
        txtThumbprintValue.DeselectAll();

    }

    private void btnAddContact_Click(object sender, EventArgs e)
    {
        ContactPerson contactPerson = new ContactPerson { Company = Contact_CompanyTextBox.Text, GivenName = Contact_GivenNameTextBox.Text, Surname = Contact_SurnameTextBox.Text, Type = (ContactType)Contact_TypeComboBox.SelectedItem };
        contactPerson.EmailAddresses.Add(this.Contact_EmailTextBox.Text);
        contactPerson.TelephoneNumbers.Add(this.Contact_TelephoneTextBox.Text);
        Organization organization = new Organization();
        organization.DisplayNames.Add(new LocalizedName(Company_DisplayName.Text, System.Globalization.CultureInfo.InvariantCulture));
        organization.Names.Add(new LocalizedName(Company_Name.Text, System.Globalization.CultureInfo.InvariantCulture));
        organization.Urls.Add(new LocalizedUri(new Uri(Company_URL.Text), System.Globalization.CultureInfo.InvariantCulture));

        this.ManifestEntity.Contacts.Add(contactPerson);
    }
	}//class

}//namespace